package tmt.usercenter.web.service;

import com.tmt.annotation.ReflectInvoked;
import com.tmt.datatable.ext.controller.SelectOptions;
import lombok.Getter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import tmt.usercenter.web.domain.*;
import tmt.usercenter.web.domain.message.NewUserRole;
import tmt.usercenter.web.repository.ITmtRoleRepo;

import javax.persistence.criteria.Predicate;
import java.util.*;
import java.util.concurrent.Future;
import java.util.stream.Collectors;

@Service
@Transactional(rollbackFor = Exception.class)
public class TmtRoleService extends TmtBaseService<TmtRole, Long> {

    @Getter
    private ITmtRoleRepo iTmtRoleRepo;

    private TmtUserService userService;

    private ClientSystemService clientSystemService;

    private TmtAuthorityService authorityService;

    private TmtRoleAuthorityService roleAuthorityService;

    @Getter
    private TmtUserRoleService userRoleService;

    private UserInfoConfigService userInfoConfigService;

    @Autowired
    public TmtRoleService(ITmtRoleRepo iTmtRoleRepo,
                          TmtUserService userService,
                          ClientSystemService clientSystemService,
                          TmtAuthorityService authorityService,
                          TmtRoleAuthorityService roleAuthorityService,
                          TmtUserRoleService userRoleService,
                          UserInfoConfigService userInfoConfigService) {
        super(iTmtRoleRepo, Arrays.asList(userService, clientSystemService,
                authorityService, roleAuthorityService, userRoleService));
        this.iTmtRoleRepo = iTmtRoleRepo;
        this.userService = userService;
        this.clientSystemService = clientSystemService;
        this.authorityService = authorityService;
        this.roleAuthorityService = roleAuthorityService;
        this.userRoleService = userRoleService;
        this.userInfoConfigService = userInfoConfigService;
    }

    @Transactional(readOnly = true)
    @Cacheable(value = "tmtRole", key = "'tmtRole' + #name", unless = "#result == null")
    public TmtRole findByName(String name) {
        return iTmtRoleRepo.findByName(name);
    }

    @Transactional(readOnly = true)
    @Cacheable(value = "tmtRole", key = "'tmtRole_findRolesByUser_' + #userId", unless = "#result == null")
    public List<TmtRole> findRolesByUserId(Long userId) {
        TmtUser user = userService.findById(userId);
        List<TmtRole> list = new ArrayList<>();
        user.getUserRoles().forEach(x -> list.add(x.getRole()));
        return list;
    }

    @Transactional(readOnly = true)
    @Cacheable(value = "tmtRole", key = "'tmtRole_findDefaultRoleByOriginSystemId_' + #systemId", unless = "#result == null")
    public TmtRole findDefaultRoleByOriginSystemId(Long systemId) {
        return iTmtRoleRepo.findDefaultRoleByOriginSystemId(systemId);
    }

    @Transactional(readOnly = true)
    @Cacheable(value = "tmtRole", key = "'tmtRole_findByCode_' + #code", unless = "#result == null")
    public TmtRole findByCode(String code) {
        return iTmtRoleRepo.findByCode(code);
    }

    @Transactional(readOnly = true)
    @ReflectInvoked("本方法被反射调用了")
    public List<TmtRole> findByClientId(String clientId) {
        Specification<TmtRole> s = (root, query, cb) -> {
            Predicate p = cb.equal(root.get("originSystem").get("clientId"), clientId);
            return query.where(p).getRestriction();
        };
        return iTmtRoleRepo.findAll(s);
    }

    @Transactional(readOnly = true)
    public TmtRole findByCodeAndClientId(String code, String clientId) {
        Specification<TmtRole> s = (root, query, cb) -> {
            Predicate p1 = cb.equal(root.get("code"), code);
            Predicate p2 = cb.equal(root.get("originSystem").get("clientId"), clientId);
            Predicate p = cb.and(p1, p2);
            return query.where(p).getRestriction();
        };
        return iTmtRoleRepo.findOne(s);
    }

    public void deleteById(Long roleId) {
        //先删除角色和权限的映射关系。
        roleAuthorityService.deleteByRoleId(roleId);
        userRoleService.deleteByRoleId(roleId);
        iTmtRoleRepo.delete(roleId);
    }

    public boolean updateRoles(List<TmtRole> roles) {
        if (roles == null || roles.size() <= 0)
            return true;

        //获取客户系统对象。
        String clientId = roles.get(0).getOriginSystem().getClientId();
        ClientSystem cs = clientSystemService.findByClientId(clientId);

        //获取客户系统的创建者。
        TmtUser temp = cs.getCreator();
        TmtUser creator = temp == null ? null : userService.findById(temp.getId());

        //获取客户系统当前所有的权限对象。
        List<TmtRole> allList = findByClientId(clientId);
        List<TmtRole> newList = new ArrayList<>();

        roles.forEach(x -> {
            Optional<TmtRole> optional = allList.stream()
                    .filter(y -> Objects.equals(y.getCode(), x.getCode()))
                    .findFirst();

            TmtRole newRole = new TmtRole();
            if (optional.isPresent()) {
                newRole = optional.get();
            } else {
                newRole.setCode(x.getCode());
                newRole.setOriginSystem(cs);
            }
            newRole.setCreator(creator);
            newRole.setName(x.getName());
            newRole.setGroupId(x.getGroupId());
            newRole.setRemark(x.getRemark());
            newRole.setDefaultRole(x.isDefaultRole());

            newList.add(newRole);
        });

        iTmtRoleRepo.save(newList);

        //构造角色权限关系记录。
        roles.forEach(x -> {
            if (x.getRoleAuthorities() == null || x.getRoleAuthorities().size() <= 0)
                return;

            TmtRole newRole = newList.stream()
                    .filter(y -> Objects.equals(y.getCode(), x.getCode()))
                    .findFirst().get();

            update(newRole, x.getRoleAuthorities());
        });

        //删除已经废弃的权限对象。
        allList.stream()
                .filter(x -> roles.stream().noneMatch(y -> Objects.equals(y.getCode(), x.getCode())))
                .forEach(x -> {
                    deleteById(x.getId());
                });

        //确保客户系统的创建者拥有所有的角色。
        //setDefaultSysAdmin(creator.getId(), cs.getClientId());

        return true;
    }

    /**
     * 将UserId指定的用户设置为客户系统的超级管理员。
     *
     * @param userId
     * @param clientId
     */
    public void setDefaultSysAdmin(Long userId, String clientId) {
        try {
            List<TmtRole> allRoles = findByClientId(clientId);
            if (allRoles == null || allRoles.size() <= 0)
                return;

            List<TmtRole> hasRoles = userRoleService.findRolesByUserIdAndClientId(userId, clientId);

            TmtUser user = userService.findById(userId);

            List<TmtUserRole> newUserRoles = new ArrayList<>();
            allRoles.stream().filter(x -> hasRoles.stream()
                    .noneMatch(y -> Objects.equals(y.getId(), x.getId())))
                    .forEach(z -> {
                        TmtUserRole userRole = new TmtUserRole();
                        userRole.setRole(z);
                        userRole.setUser(user);
                        newUserRoles.add(userRole);
                    });
            userRoleService.createAll(newUserRoles);
        } catch (Exception ex) {
        }
    }

    /**
     * 更新指定角色的所属权限
     *
     * @param role
     * @param roleAuthorities
     */
    public void update(TmtRole role, List<TmtRoleAuthority> roleAuthorities) {
        try {
            List<TmtRoleAuthority> newList = new ArrayList<>();
            List<TmtRoleAuthority> curAllList = roleAuthorityService.findByRoleId(role.getId());
            roleAuthorities.forEach(x -> {
                Optional<TmtRoleAuthority> optional = curAllList.stream()
                        .filter(y -> y.getAuthority() != null && x.getAuthority() != null &&
                                Objects.equals(y.getAuthority().getAuthority(), x.getAuthority().getAuthority()))
                        .findFirst();
                if (optional.isPresent())
                    return;

                TmtAuthority authority = authorityService.findByAuthority(x.getAuthority().getAuthority());
                if (authority != null) {
                    TmtRoleAuthority newRoleAuthority = new TmtRoleAuthority();
                    newRoleAuthority.setRole(role);
                    newRoleAuthority.setAuthority(authority);
                    newList.add(newRoleAuthority);
                }
            });

            roleAuthorityService.createAll(newList);

            //删除已经废弃的角色权限映射对象。
            curAllList.stream()
                    .filter(x -> roleAuthorities.stream().noneMatch(y -> y.getAuthority() != null && x.getAuthority() != null
                            && Objects.equals(y.getAuthority().getAuthority(), x.getAuthority().getAuthority())))
                    .forEach(x -> {
                        try {
                            roleAuthorityService.delete(x);
                        } catch (Exception ex) {
                        }
                    });
        } catch (Exception ex) {
        }

    }

    public void deleteBy(NewUserRole newUserRole) {
        userRoleService.deleteBy(newUserRole.getClientId(), newUserRole.getUserId(), newUserRole.getRoles());
    }

    public void createBy(NewUserRole newUserRole) throws Exception {
        TmtUser user = userService.findById(newUserRole.getUserId());
        if (user == null)
            throw new Exception("用户不存在");

        List<String> codes = newUserRole.getRoles().stream().distinct().collect(Collectors.toList());

        List<TmtRole> list = findByClientId(newUserRole.getClientId());
        list = list.stream().filter(x -> codes.contains(x.getCode())).collect(Collectors.toList());
        if (list.size() <= 0 || list.size() != codes.size())
            throw new Exception("角色不存在");

        List<TmtUserRole> userRoles = new ArrayList<>();

        list.forEach(x -> {
            if (!userRoleService.existsByUserAndRole(user, x))
                userRoles.add(new TmtUserRole(user, x));
        });

        userRoleService.createAll(userRoles);
    }

    public void updateBy(NewUserRole newUserRole) throws Exception {
        TmtUser user = userService.findById(newUserRole.getUserId());
        if (user == null)
            throw new Exception("用户不存在");

        List<TmtUserRole> userRoles = userRoleService.findAllByUserIdAndClientId(newUserRole.getUserId(), newUserRole.getClientId());

        //先删除不存在的记录。
        List<String> codes = newUserRole.getRoles().stream().distinct().collect(Collectors.toList());
        List<TmtUserRole> deletedList = new ArrayList<>();
        userRoles.forEach(x -> {
            try {
                if (!codes.contains(x.getRole().getCode())) {
                    deletedList.add(x);
                }
            } catch (Exception ignored) {
            }
        });

        userRoleService.deleteAll(deletedList);
        userRoles.removeAll(deletedList);


        //再创建新增的用户权限记录。
        List<TmtRole> list = findByClientId(newUserRole.getClientId());
        List<TmtUserRole> newList = new ArrayList<>();
        codes.stream().filter(x -> userRoles.stream().noneMatch(y -> Objects.equals(y.getRole().getCode(), x)))
                .forEach(x -> {
                    Optional<TmtRole> role = list.stream().filter(y -> Objects.equals(y.getCode(), x)).findFirst();
                    role.ifPresent(tmtRole -> newList.add(new TmtUserRole(user, tmtRole)));
                });

        userRoleService.createAll(newList);
    }

    @Override
    public Future<SelectOptions> findForSelect2(Class<TmtRole> clazz, String idProp, String propName,
                                                boolean displayExtendInfo, String search, String tag, Pageable pageable,
                                                Map<String, String[]> filterConditions,
                                                Map<String, String[]> limitedConditions,
                                                Map<String, String[]> originParams) {
        if (filterConditions.containsKey("originSystem.id")) {
            try {
                String[] oldParams = filterConditions.get("originSystem.id");
                Long id = Long.parseLong(oldParams[0]);
                if (!iTmtRoleRepo.existsByOriginSystemId(id)) {
                    UserInfoConfig uic = userInfoConfigService.getIUserRegisterConfigRepo().findByOriginSystemId(id);
                    if (uic != null && !StringUtils.isEmpty(uic.getClientGroupId())) {
                        List<UserInfoConfig> list = userInfoConfigService.getIUserRegisterConfigRepo().findAllByClientGroupId(uic.getClientGroupId());
                        if (list.size() > 1) {
                            String[] params = new String[list.size()];
                            for (int i = 0; i < list.size(); i++) {
                                params[i] = list.get(i).getOriginSystem().getId().toString();
                            }
                            filterConditions.replace("originSystem.id", oldParams, params);
                        }
                    }
                }
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }

        return super.findForSelect2(clazz, idProp, propName, displayExtendInfo,
                search, tag, pageable, filterConditions, limitedConditions, originParams);
    }
}
